﻿using System;
using System.Collections.Generic;
using System.Linq;
using AreaEdit.ModifiedAreat;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.ExtensibleStorage;
using Autodesk.Revit.UI;
using YArchitech.Revit;
using YJKArchMethodLibrary.Utils;

namespace AreaEdit.AreaSet
{
	public class AreaLoopSearcher
	{
		public AreaLoopSearcher(ExternalCommandData cmdData)
		{
			this.m_Revit = cmdData;
			this.m_document = this.m_Revit.Application.ActiveUIDocument.Document;
			this.m_OffsetLine = new OffsetWallLine(this.m_Revit, "OutLine");
			this.m_Options = this.m_Revit.Application.Application.Create.NewGeometryOptions();
			this.m_Options.View = this.m_Revit.Application.ActiveUIDocument.Document.ActiveView;
		}

		public bool SearchFillAreaLoops_IncludColumn(List<Room> rooms, bool searchBalcony, bool includBalcony, string RangeType, List<Wall> lstOutWall, ref List<Area> areaList, ref List<ElementId> roomIdList, ref string message)
		{
			return this.SearchAreaLoops_ManyALittleMakesAMickle(rooms, EdgePosition.EP_Center, true, searchBalcony, RangeType, lstOutWall, ref areaList, includBalcony, ref roomIdList, ref message);
		}

		public bool SearchAreaLoops(List<Room> rooms, EdgePosition edgePos, bool searchBalcony, bool includBalcony, string RangeType, List<Wall> lstOutWall, ref List<Area> areaList, ref List<ElementId> roomIdList, ref string message)
		{
			return this.SearchAreaLoops_ManyALittleMakesAMickle(rooms, edgePos, false, searchBalcony, RangeType, lstOutWall, ref areaList, includBalcony, ref roomIdList, ref message);
		}

		public bool SearchNetAreaLoops(List<Room> rooms, ref string message)
		{
			Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			Settings settings = this.m_Revit.Application.ActiveUIDocument.Document.Settings;
			int location = RevitVersionFuncs.SetRoomAreaBoundaryLocation(settings, document, 0);
			ViewPlan curViewPlan = this.m_Revit.Application.ActiveUIDocument.ActiveView as ViewPlan;
			new LoopSearcher(this.m_Revit);
			new List<Pole>();
			Transaction transaction = null;
			transaction = new Transaction(document);
			transaction.Start("create area");
			foreach (Room room in rooms)
			{
				SpatialElementBoundaryOptions spatialElementBoundaryOptions = new SpatialElementBoundaryOptions();
				IList<IList<BoundarySegment>> boundarySegments = room.GetBoundarySegments(spatialElementBoundaryOptions);
				List<Curve> list = new List<Curve>();
				foreach (IList<BoundarySegment> list2 in boundarySegments)
				{
					foreach (BoundarySegment segment in list2)
					{
						Curve curve = segment.GetCurve();
						list.Add(curve);
					}
				}
				try
				{
					CurveArray curveArray = this.m_Revit.Application.Application.Create.NewCurveArray();
					foreach (Curve curve2 in list)
					{
						curveArray.Append(curve2);
					}
					this.CreateArea(curveArray, curViewPlan);
				}
				catch (Exception)
				{
				}
			}
			transaction.Commit();
			RevitVersionFuncs.SetRoomAreaBoundaryLocation(settings, document, location);
			return true;
		}

		private bool SearchAreaLoops_ManyALittleMakesAMickle(List<Room> rooms, EdgePosition edgePos, bool includColumn, bool searchBalcony, string RangeType, List<Wall> lstAllOutWalls, ref List<Area> areaList, bool includBalcony, ref List<ElementId> roomIdList, ref string message)
		{
			LoopSearcher loopSearcher = new LoopSearcher(this.m_Revit);
			List<Pole> list = new List<Pole>();
			new List<Curve>();
			new List<Curve>();
			new List<FamilyInstance>();
			foreach (Room room in rooms)
			{
				SpatialElementBoundaryOptions spatialElementBoundaryOptions = new SpatialElementBoundaryOptions();
				IList<IList<BoundarySegment>> boundarySegments = room.GetBoundarySegments(spatialElementBoundaryOptions);
				List<Pole> list2 = new List<Pole>();
				foreach (IList<BoundarySegment> boundary in boundarySegments)
				{
					List<Pole> polesByRoomBoundary = this.GetPolesByRoomBoundary(boundary, includColumn);
					list2.AddRange(polesByRoomBoundary);
				}
				List<List<Pole>> list3 = new List<List<Pole>>();
				if (loopSearcher.GetBoundaryPoles(list2, EdgePosition.EP_Center, ref list3))
				{
					roomIdList.Add(room.Id);
					foreach (List<Pole> list4 in list3)
					{
						using (List<Pole>.Enumerator enumerator4 = list4.GetEnumerator())
						{
							while (enumerator4.MoveNext())
							{
								Pole pole = enumerator4.Current;
								int num = list.FindIndex((Pole value) => value.SameAs(pole));
								if (num == -1)
								{
									list.Add(pole);
								}
							}
						}
					}
				}
			}
			if (list.Count == 0)
			{
				message = "未发现房间边界，请先设置房间";
				return false;
			}
			ViewPlan curViewPlan = this.m_Revit.Application.ActiveUIDocument.ActiveView as ViewPlan;
			List<List<CurveInformationBean>> boundaryEdgeBeanLoops = loopSearcher.GetBoundaryEdgeBeanLoops(list, edgePos);
			OffsetWallLine offsetWallLine = new OffsetWallLine(this.m_Revit, "CenterLine");
			List<Wall> outWallsCurViewPlan = offsetWallLine.GetOutWallsCurViewPlan(this.m_document.ActiveView as ViewPlan, this.m_document);
			Transaction transaction = null;
			try
			{
				transaction = new Transaction(this.m_Revit.Application.ActiveUIDocument.Document);
				transaction.Start("create area");
				for (int i = 0; i < boundaryEdgeBeanLoops.Count; i++)
				{
					new List<Wall>();
					new List<Curve>();
					CurveArray curveArray = this.m_Revit.Application.Application.Create.NewCurveArray();
					List<List<Curve>> curveAryForBuildingArea = offsetWallLine.GetCurveAryForBuildingArea(boundaryEdgeBeanLoops, rooms, RangeType, outWallsCurViewPlan);
					foreach (List<Curve> list5 in curveAryForBuildingArea)
					{
						curveArray.Clear();
						foreach (Curve curve in list5)
						{
							curveArray.Append(curve);
						}
						Area area = this.CreateArea(curveArray, curViewPlan);
						if (area != null)
						{
							areaList.Add(area);
						}
					}
				}
				transaction.Commit();
			}
			catch (Exception ex)
			{
				message = ex.Message;
				return false;
			}
			return true;
		}

		private List<Pole> GetPolesByRoomBoundary(IList<BoundarySegment> boundary, bool includColumn)
		{
			Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			List<Pole> list = new List<Pole>();
			List<Pole> poles = new List<Pole>();
			List<int> list2 = new List<int>();
			List<List<Pole>> list3 = new List<List<Pole>>();
			List<int> list4 = new List<int>();
			List<KeyValuePair<ElementId, int>> list5 = new List<KeyValuePair<ElementId, int>>();
			new List<Wall>();
			new List<FamilyInstance>();
			int num = 0;
			ElementId elementId = null;
			Element tempElement = null;
			foreach (BoundarySegment boundarySegment in boundary)
			{
				elementId = null;
				tempElement = null;
				RevitLinkInstance revitLinkInstance = null;
				ElementId boundarySegmentElementId = RevitElementExtension.GetBoundarySegmentElementId(boundarySegment, out elementId);
				if (null != boundarySegmentElementId)
				{
					tempElement = this.m_document.GetElement(boundarySegmentElementId);
				}
				if (tempElement != null && tempElement is RevitLinkInstance)
				{
					revitLinkInstance = (tempElement as RevitLinkInstance);
					if (null != elementId && revitLinkInstance.GetLinkDocument() != null)
					{
						tempElement = revitLinkInstance.GetLinkDocument().GetElement(elementId);
					}
				}
				else
				{
					tempElement = boundarySegment.GetElement(document);
				}
				if (tempElement != null)
				{
					int num2 = list5.FindIndex((KeyValuePair<ElementId, int> idInfo) => idInfo.Key.IntegerValue == tempElement.Id.IntegerValue);
					if (num2 < 0)
					{
						KeyValuePair<ElementId, int> item = new KeyValuePair<ElementId, int>(tempElement.Id, list5.Count);
						list5.Add(item);
						if (tempElement.Category.Id.IntegerValue == -2000100 || tempElement.Category.Id.IntegerValue == -2001330)
						{
							list4.Add(num);
							num++;
							List<Pole> list6 = new List<Pole>();
							this.GetColumnEdges(tempElement as FamilyInstance, ref list6, revitLinkInstance);
							list3.Add(list6);
							if (includColumn)
							{
								list.AddRange(list6);
							}
						}
						if (this.GetPoleByRoomBoundarySegment(boundarySegment, tempElement, revitLinkInstance, ref poles))
						{
							list2.Add(num);
							num++;
						}
					}
				}
			}
			this.IntersectContinuePoles(poles, list2, list3, list4, ref list);
			return list;
		}

		private bool GetPoleByRoomBoundarySegment(BoundarySegment segment, Element inputElement, RevitLinkInstance inputRevitLinkInstance, ref List<Pole> poles)
		{
			Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			Element element = inputElement;
			if (element == null)
			{
				return false;
			}
			int num = poles.FindIndex((Pole p) => p.ComponentId.IntegerValue == element.Id.IntegerValue);
			if (num != -1)
			{
				return false;
			}
			if (element.GetType() == typeof(Wall))
			{
				Wall wall = element as Wall;
				Pole item = Pole.TransformWallToPole(wall, inputRevitLinkInstance);
				poles.Add(item);
			}
			else
			{
				if (!(element.GetType() == typeof(ModelLine)) && !(element.GetType() == typeof(ModelArc)))
				{
					return false;
				}
				ModelCurve modelCurve = element as ModelCurve;
				Curve geometryCurve = modelCurve.GeometryCurve;
				if (geometryCurve.GetType() == typeof(Line))
				{
					Line line = geometryCurve as Line;
					if (inputRevitLinkInstance != null)
					{
						line = (line.CreateTransformed(inputRevitLinkInstance.GetTotalTransform()) as Line);
					}
					XYZ hyendPoint = line.GetEndPoint(0);
					XYZ hyendPoint2 = line.GetEndPoint(1);
					Pole item2 = Pole.NewPole(element, inputRevitLinkInstance, hyendPoint, hyendPoint2, 0.0, 0.0);
					poles.Add(item2);
				}
				else if (geometryCurve.GetType() == typeof(Arc))
				{
					Arc arc = geometryCurve as Arc;
					if (inputRevitLinkInstance != null)
					{
						arc = (arc.CreateTransformed(inputRevitLinkInstance.GetTotalTransform()) as Arc);
					}
					XYZ hyendPoint3 = arc.GetEndPoint(0);
					XYZ hyendPoint4 = arc.GetEndPoint(1);
					XYZ center = arc.Center;
					XYZ normal = arc.Normal;
					Pole item3 = Pole.NewPole(element, inputRevitLinkInstance, hyendPoint3, hyendPoint4, center, normal, 0.0, 0.0);
					poles.Add(item3);
				}
			}
			return true;
		}

		private void IntersectContinuePoles(List<Pole> poles, List<int> edgesIndexList, List<List<Pole>> columnsEdges, List<int> columnsIndexList, ref List<Pole> poleLoops)
		{
			Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			for (int i = 0; i < poles.Count; i++)
			{
				Pole pole = poles[i];
				poleLoops.Add(pole);
				int num = edgesIndexList[i];
				Pole pole2 = poles[0];
				int num2 = edgesIndexList[0];
				if (i != poles.Count - 1)
				{
					pole2 = poles[i + 1];
					num2 = edgesIndexList[i + 1];
				}
				if (num2 < num)
				{
					num2 += edgesIndexList.Count + columnsIndexList.Count;
				}
				XYZ ptStart = null;
				XYZ ptEnd = null;
				this.IsLinked(pole.StartPoint, pole.EndPoint, pole2.StartPoint, pole2.EndPoint, ref ptStart, ref ptEnd);
				XYZ refPoint = Geometry.CalculatMidPoint(ptStart, ptEnd);
				XYZ xyz = null;
				if (!this.Intersect2Pole(document, pole, pole2, true, refPoint, ref xyz))
				{
					if (this.Intersect2Pole(document, pole, pole2, false, refPoint, ref xyz))
					{
						int j = 0;
						int num3 = -1;
						while (j < columnsIndexList.Count)
						{
							int num4 = columnsIndexList[j];
							if (num4 == 0)
							{
								num4 = edgesIndexList.Count + columnsIndexList.Count;
							}
							if (num4 > num && num4 < num2)
							{
								num3 = j;
								break;
							}
							j++;
						}
						if (num3 != -1 && !this.PointInsideColumn(xyz, columnsEdges[num3]))
						{
							Pole item = Pole.NewPole(ElementId.InvalidElementId, ptStart, ptEnd, 0.0, 0.0);
							poleLoops.Add(item);
						}
						else
						{
							Pole item2 = Pole.NewPole(pole.Element, pole.UseRevitLinkInstance, ptStart, xyz, pole.PositiveHalfWidth, pole.NegativeHalfWidth);
							poleLoops.Add(item2);
							Pole item3 = Pole.NewPole(pole2.Element, pole2.UseRevitLinkInstance, xyz, ptEnd, pole2.PositiveHalfWidth, pole2.NegativeHalfWidth);
							poleLoops.Add(item3);
						}
					}
					else
					{
						Pole item4 = Pole.NewPole(ElementId.InvalidElementId, ptStart, ptEnd, 0.0, 0.0);
						poleLoops.Add(item4);
					}
				}
			}
		}

		private bool PointInsideColumn(XYZ ptInter, List<Pole> columnEdgeLoop)
		{
			List<XYZ> list = new List<XYZ>();
			List<double> list2 = new List<double>();
			double num = 0.0;
			foreach (Pole pole in columnEdgeLoop)
			{
				list.Add(pole.StartPoint);
				list2.Add(pole.Bulge);
				num += pole.Length();
			}
			List<XYZ> vecPolypt = new List<XYZ>();
			double fitSpace = num / 10.0;
			if (!Geometry.GetFittingPolyline(list, list2, ref vecPolypt, fitSpace))
			{
				return false;
			}
			int num2 = Geometry.PointInPloygon(vecPolypt, ptInter, 0.0001, 0.0001);
			return num2 != -1;
		}

		private bool Intersect2Pole(Autodesk.Revit.DB.Document doc, Pole pole1, Pole pole2, bool isBound, XYZ refPoint, ref XYZ ptInter)
		{
			Curve curve = pole1.ToCurve(doc, isBound);
			Curve curve2 = pole2.ToCurve(doc, isBound);
			List<XYZ> list = new List<XYZ>();
			IntersectionResultArray intersectionResultArray;
			SetComparisonResult setComparisonResult = curve.Intersect(curve2, out intersectionResultArray);
			if (setComparisonResult == SetComparisonResult.Overlap)
			{
				foreach (object obj in intersectionResultArray)
				{
					IntersectionResult intersectionResult = (IntersectionResult)obj;
					list.Add(intersectionResult.XYZPoint);
				}
			}
			if (list.Count == 1)
			{
				double val = AssistFunc.mmToFeet(50000.0);
				double val2 = refPoint.DistanceTo(list[0]);
				if (Geometry.GreaterThan(val2, val))
				{
					return false;
				}
				ptInter = list[0];
				return true;
			}
			else
			{
				if (list.Count == 2)
				{
					double val3 = refPoint.DistanceTo(list[0]);
					double val4 = refPoint.DistanceTo(list[1]);
					ptInter = list[1];
					if (Geometry.LessThan(val3, val4))
					{
						ptInter = list[0];
					}
					return true;
				}
				return false;
			}
		}

		private void GetColumnEdges(FamilyInstance element, ref List<Pole> columnEdges, RevitLinkInstance inputRevitLinkInstance = null)
		{
			GeometryElement geometryElement = element.GetOriginalGeometry(this.m_Options);
			geometryElement = geometryElement.GetTransformed(element.GetTransform());
			if (null == geometryElement)
			{
				return;
			}
			if (inputRevitLinkInstance != null)
			{
				geometryElement = geometryElement.GetTransformed(inputRevitLinkInstance.GetTransform());
				if (null == geometryElement)
				{
					return;
				}
			}
			foreach (GeometryObject geometryObject in geometryElement)
			{
				if (!(geometryObject.GetType() != typeof(Solid)))
				{
					Solid solid = geometryObject as Solid;
					FaceArray faces = solid.Faces;
					foreach (object obj in faces)
					{
						Face face = (Face)obj;
						if (!(face.GetType() != typeof(PlanarFace)))
						{
							PlanarFace planarFace = face as PlanarFace;
							if (planarFace.FaceNormal().IsAlmostEqualTo(XYZ.BasisZ))
							{
								EdgeArrayArray edgeLoops = planarFace.EdgeLoops;
								this.GetLoopsEdge(edgeLoops, element, inputRevitLinkInstance, ref columnEdges);
							}
						}
					}
				}
			}
		}

		private void GetLoopsEdge(EdgeArrayArray edgeAryAry, Element inputElemnt, RevitLinkInstance inputRevitLinkInstance, ref List<Pole> columnEdges)
		{
			foreach (object obj in edgeAryAry)
			{
				EdgeArray edgeArray = (EdgeArray)obj;
				foreach (object obj2 in edgeArray)
				{
					Edge edge = (Edge)obj2;
					Curve curve = edge.AsCurve();
					Pole item = Pole.TransformWallToPole(curve, inputElemnt, inputRevitLinkInstance, 0.0, 0.0);
					columnEdges.Add(item);
				}
			}
		}

		private bool IsLinked(XYZ ptS1, XYZ ptE1, XYZ ptS2, XYZ ptE2, ref XYZ ptNear1, ref XYZ ptNear2)
		{
			if (ptS1.IsAlmostEqualTo(ptS2))
			{
				ptNear1 = ptS1;
				ptNear2 = ptS2;
				return true;
			}
			if (ptS1.IsAlmostEqualTo(ptE2))
			{
				ptNear1 = ptS1;
				ptNear2 = ptE2;
				return true;
			}
			if (ptE1.IsAlmostEqualTo(ptS2))
			{
				ptNear1 = ptE1;
				ptNear2 = ptS2;
				return true;
			}
			if (ptE1.IsAlmostEqualTo(ptE2))
			{
				ptNear1 = ptE1;
				ptNear2 = ptE2;
				return true;
			}
			List<XYZ> list = new List<XYZ>();
			List<XYZ> list2 = new List<XYZ>();
			double num = ptS1.DistanceTo(ptS2);
			list.Add(ptS1);
			list2.Add(ptS2);
			double num2 = ptE1.DistanceTo(ptS2);
			list.Add(ptE1);
			list2.Add(ptS2);
			double num3 = ptS1.DistanceTo(ptE2);
			list.Add(ptS1);
			list2.Add(ptE2);
			double num4 = ptE1.DistanceTo(ptE2);
			list.Add(ptE1);
			list2.Add(ptE2);
			int num5 = 0;
			double val = num;
			if (Geometry.LessThan(num2, num))
			{
				val = num2;
				num5 = 1;
			}
			int num6 = 2;
			double val2 = num3;
			if (Geometry.LessThan(num4, num3))
			{
				val2 = num4;
				num6 = 3;
			}
			int index = num5;
			if (Geometry.LessThan(val2, val))
			{
				index = num6;
			}
			ptNear1 = list[index];
			ptNear2 = list2[index];
			return false;
		}

		private Area CreateArea(CurveArray curves, ViewPlan curViewPlan)
		{
			List<ElementId> list = new List<ElementId>();
			List<Curve> list2 = new List<Curve>();
			UIApplication application = this.m_Revit.Application;
			UIDocument activeUIDocument = application.ActiveUIDocument;
			Document document = activeUIDocument.Document;
			foreach (object obj in curves)
			{
				Curve curve = (Curve)obj;
				if (curve != null)
				{
					list2.Add(curve);
				}
			}
			SketchPlane sketchPlane = curViewPlan.SketchPlane;
			XYZ xyz = this.m_OffsetLine.Centroid(list2);
			foreach (Curve curve2 in list2)
			{
				ModelCurve modelCurve = document.Create.NewAreaBoundaryLine(sketchPlane, curve2, curViewPlan);
				list.Add(modelCurve.Id);
			}
			UV uv = new UV(xyz.X, xyz.Y);
			Area area = document.Create.NewArea(curViewPlan, uv);
			AreaTag areaTag = document.Create.NewAreaTag(curViewPlan, area, uv);
			AreaTagType areaTagType = this.GetAreaTagType();
			if (areaTagType != null)
			{
				areaTag.AreaTagType = areaTagType;
			}
			this.WriteBoundaryIdToArea(area, list);
			return area;
		}

		private void WriteBoundaryIdToArea(Element element, List<ElementId> boundaryIds)
		{
			Guid guid = new Guid(AreaLoopSearcher.m_Guid);
			Schema schema = Schema.Lookup(guid);
			if (schema == null)
			{
				SchemaBuilder schemaBuilder = new SchemaBuilder(guid);
				schemaBuilder.SetSchemaName("BoundaryElementId_XData");
				schemaBuilder.AddArrayField("Boundary", typeof(ElementId));
				schema = schemaBuilder.Finish();
			}
			Entity entity = new Entity(schema);
			Field field = schema.GetField("Boundary");
			entity.Set<IList<ElementId>>(field, boundaryIds);
			element.SetEntity(entity);
		}

		public static List<ElementId> ReadAreaData(Element element)
		{
			try
			{
				Guid guid = new Guid(AreaLoopSearcher.m_Guid);
				Schema schema = Schema.Lookup(guid);
				if (schema != null)
				{
					Entity entity = element.GetEntity(schema);
					if (entity != null && entity.Schema != null)
					{
						Field field = schema.GetField("Boundary");
						return entity.Get<IList<ElementId>>(field).ToList<ElementId>();
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private AreaTagType GetAreaTagType()
		{
			AreaTagType areaTagType = null;
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_Revit.Application.ActiveUIDocument.Document);
			IList<Element> list = filteredElementCollector.OfClass(typeof(FamilySymbol)).ToElements();
			foreach (Element element in list)
			{
				AreaTagType areaTagType2 = element as AreaTagType;
				if (areaTagType2 != null)
				{
					if (areaTagType == null)
					{
						areaTagType = areaTagType2;
					}
					if (areaTagType2.Name.IndexOf("标记") != -1)
					{
						return areaTagType2;
					}
				}
			}
			return null;
		}

		private XYZ GetInternalPointByLoop(CurveArray curves)
		{
			XYZ result = new XYZ(0.0, 0.0, 0.0);
			if (curves.Size < 2)
			{
				return result;
			}
			return Geometry.Centroid(curves);
		}

		private ExternalCommandData m_Revit;

		private Document m_document;

		private Options m_Options;

		private List<Wall> lstSelWalls = new List<Wall>();

		private OffsetWallLine m_OffsetLine;

		private CurveArray tmpCA;

		private List<Dictionary<Wall, Curve>> dicEachWallToCurve = new List<Dictionary<Wall, Curve>>();

		private static string m_Guid = "772B74F8-FB48-43C7-9E4F-B17036EF2895";
	}
}
